/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */

import type {JSX} from 'react';
import {useEffect, useRef, useState} from 'react';

import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {$wrapNodeInElement, mergeRegister} from '@lexical/utils';
import {
    $createParagraphNode,
    $createRangeSelection,
    $getSelection,
    $insertNodes,
    $isNodeSelection,
    $isRootOrShadowRoot,
    $setSelection,
    COMMAND_PRIORITY_EDITOR,
    COMMAND_PRIORITY_HIGH,
    COMMAND_PRIORITY_LOW,
    createCommand,
    DRAGOVER_COMMAND,
    DRAGSTART_COMMAND,
    DROP_COMMAND,
    getDOMSelectionFromTarget,
    isHTMLElement,
    LexicalCommand,
    LexicalEditor,
} from 'lexical';


import landscapeImage from '../../images/landscape.jpg';
import yellowFlowerImage from '../../images/yellow-flower.jpg';
import {$createImageNode, $isImageNode, ImageNode, ImagePayload,} from '../../nodes/ImageNode';
import Button from '../../ui/Button';
import {DialogActions, DialogButtonsList} from '../../ui/Dialog';
import FileInput from '../../ui/FileInput';
import TextInput from '../../ui/TextInput';
import {useSettings} from "../../../settings/SettingsContext.tsx";
import {copyFile, exists, mkdir} from "@tauri-apps/plugin-fs";
import {basename, dirname, join} from '@tauri-apps/api/path';
import {useFile} from "../../context/FileContext.tsx";

export type InsertImagePayload = Readonly<ImagePayload>;

export const INSERT_IMAGE_COMMAND: LexicalCommand<InsertImagePayload> =
    createCommand('INSERT_IMAGE_COMMAND');

export function InsertImageUriDialogBody({
                                             onClick,
                                         }: {
    onClick: (payload: InsertImagePayload) => void;
}) {
    const [src, setSrc] = useState('');
    const [altText, setAltText] = useState('');

    const isDisabled = src === '';

    return (
        <>
            <TextInput
                label="Image URL"
                placeholder="i.e. https://source.unsplash.com/random"
                onChange={setSrc}
                value={src}
                data-test-id="image-modal-url-input"
            />
            <TextInput
                label="Alt Text"
                placeholder="Random unsplash image"
                onChange={setAltText}
                value={altText}
                data-test-id="image-modal-alt-text-input"
            />
            <DialogActions>
                <Button
                    data-test-id="image-modal-confirm-btn"
                    disabled={isDisabled}
                    onClick={() => onClick({altText, src})}>
                    Confirm
                </Button>
            </DialogActions>
        </>
    );
}

export function InsertImageUploadedDialogBody({
                                                  onClick,
                                              }: {
    onClick: (payload: InsertImagePayload) => void;
}) {
    const [src, setSrc] = useState('');
    const [altText, setAltText] = useState('');
    const settings = useSettings();
    const file = useFile();
    const isDisabled = src === '';

    const loadImage = async (files: string | null) => {
        if (!files) {
            return;
        }
        console.log('settings', settings)
        const imageFileBaseName = await basename(files);
        if (settings.image.action === 1) {
            if (settings.image.preferRelativeFolder) {
                const mdFileName = await basename(file.path, ".md");
                let dirName = mdFileName + '.assets'
                if (settings.image.relativeFolderName) {
                    dirName = settings.image.relativeFolderName.replace('${filename}', mdFileName)
                }
                const toDir = await join(await dirname(file.path), dirName);
                const dirExists = await exists(toDir);
                if (!dirExists) {
                    await mkdir(toDir, {recursive: true});
                }
                const toPath = await join(toDir, imageFileBaseName)
                await copyFile(files, toPath);
                setSrc(await join(dirName, imageFileBaseName))
            } else {
                if (settings.image.globalDir) {
                    const dirExists = await exists(settings.image.globalDir);
                    if (!dirExists) {
                        await mkdir(settings.image.globalDir, {recursive: true});
                    }
                    const toPath = await join(settings.image.globalDir, imageFileBaseName);
                    await copyFile(files, toPath);
                    setSrc(toPath)
                } else {
                    setSrc(files);
                }
            }
        } else {
            setSrc(files);
        }
        // const reader = new FileReader();
        // reader.onload = function () {
        //   if (typeof reader.result === 'string') {
        //     setSrc(reader.result);
        //   }
        //   return '';
        // };
        // if (files !== null) {
        //   reader.readAsDataURL(files[0]);
        // }
    };

    return (
        <>
            <FileInput
                label="Image"
                onChange={loadImage}
                accept="image/*"
                data-test-id="image-modal-file-upload"
            />
            <TextInput
                label="url"
                placeholder="Descriptive alternative text"
                onChange={() => {
                }}
                value={src}
                data-test-id="image-modal-alt-text-input"
            />
            <TextInput
                label="Alt Text"
                placeholder="Descriptive alternative text"
                onChange={setAltText}
                value={altText}
                data-test-id="image-modal-alt-text-input"
            />
            <DialogActions>
                <Button
                    data-test-id="image-modal-file-upload-btn"
                    disabled={isDisabled}
                    onClick={() => onClick({altText, src})}>
                    Confirm
                </Button>
            </DialogActions>
        </>
    );
}

export function InsertImageDialog({
                                      activeEditor,
                                      onClose,
                                  }: {
    activeEditor: LexicalEditor;
    onClose: () => void;
}): JSX.Element {
    const [mode, setMode] = useState<null | 'url' | 'file'>(null);
    const hasModifier = useRef(false);

    useEffect(() => {
        hasModifier.current = false;
        const handler = (e: KeyboardEvent) => {
            hasModifier.current = e.altKey;
        };
        document.addEventListener('keydown', handler);
        return () => {
            document.removeEventListener('keydown', handler);
        };
    }, [activeEditor]);

    const onClick = (payload: InsertImagePayload) => {
        activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, payload);
        onClose();
    };

    return (
        <>
            {!mode && (
                <DialogButtonsList>
                    <Button
                        data-test-id="image-modal-option-sample"
                        onClick={() =>
                            onClick(
                                hasModifier.current
                                    ? {
                                        altText:
                                            'Daylight fir trees forest glacier green high ice landscape',
                                        src: landscapeImage,
                                    }
                                    : {
                                        altText: 'Yellow flower in tilt shift lens',
                                        src: yellowFlowerImage,
                                    },
                            )
                        }>
                        Sample
                    </Button>
                    <Button
                        data-test-id="image-modal-option-url"
                        onClick={() => setMode('url')}>
                        URL
                    </Button>
                    <Button
                        data-test-id="image-modal-option-file"
                        onClick={() => setMode('file')}>
                        File
                    </Button>
                </DialogButtonsList>
            )}
            {mode === 'url' && <InsertImageUriDialogBody onClick={onClick}/>}
            {mode === 'file' && <InsertImageUploadedDialogBody onClick={onClick}/>}
        </>
    );
}

export default function ImagesPlugin({
                                         captionsEnabled,
                                     }: {
    captionsEnabled?: boolean;
}): JSX.Element | null {
    const [editor] = useLexicalComposerContext();

    useEffect(() => {
        if (!editor.hasNodes([ImageNode])) {
            throw new Error('ImagesPlugin: ImageNode not registered on editor');
        }

        return mergeRegister(
            editor.registerCommand<InsertImagePayload>(
                INSERT_IMAGE_COMMAND,
                (payload) => {
                    const imageNode = $createImageNode(payload);
                    $insertNodes([imageNode]);
                    if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) {
                        $wrapNodeInElement(imageNode, $createParagraphNode).selectEnd();
                    }

                    return true;
                },
                COMMAND_PRIORITY_EDITOR,
            ),
            editor.registerCommand<DragEvent>(
                DRAGSTART_COMMAND,
                (event) => {
                    return $onDragStart(event);
                },
                COMMAND_PRIORITY_HIGH,
            ),
            editor.registerCommand<DragEvent>(
                DRAGOVER_COMMAND,
                (event) => {
                    return $onDragover(event);
                },
                COMMAND_PRIORITY_LOW,
            ),
            editor.registerCommand<DragEvent>(
                DROP_COMMAND,
                (event) => {
                    return $onDrop(event, editor);
                },
                COMMAND_PRIORITY_HIGH,
            ),
        );
    }, [captionsEnabled, editor]);

    return null;
}

const TRANSPARENT_IMAGE =
    '';
const img = document.createElement('img');
img.src = TRANSPARENT_IMAGE;

function $onDragStart(event: DragEvent): boolean {
    const node = $getImageNodeInSelection();
    if (!node) {
        return false;
    }
    const dataTransfer = event.dataTransfer;
    if (!dataTransfer) {
        return false;
    }
    dataTransfer.setData('text/plain', '_');
    dataTransfer.setDragImage(img, 0, 0);
    dataTransfer.setData(
        'application/x-lexical-drag',
        JSON.stringify({
            data: {
                altText: node.__altText,
                caption: node.__caption,
                height: node.__height,
                key: node.getKey(),
                maxWidth: node.__maxWidth,
                showCaption: node.__showCaption,
                src: node.__src,
                width: node.__width,
            },
            type: 'image',
        }),
    );

    return true;
}

function $onDragover(event: DragEvent): boolean {
    const node = $getImageNodeInSelection();
    if (!node) {
        return false;
    }
    if (!canDropImage(event)) {
        event.preventDefault();
    }
    return true;
}

function $onDrop(event: DragEvent, editor: LexicalEditor): boolean {
    const node = $getImageNodeInSelection();
    if (!node) {
        return false;
    }
    const data = getDragImageData(event);
    if (!data) {
        return false;
    }
    event.preventDefault();
    if (canDropImage(event)) {
        const range = getDragSelection(event);
        node.remove();
        const rangeSelection = $createRangeSelection();
        if (range !== null && range !== undefined) {
            rangeSelection.applyDOMRange(range);
        }
        $setSelection(rangeSelection);
        editor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
    }
    return true;
}

function $getImageNodeInSelection(): ImageNode | null {
    const selection = $getSelection();
    if (!$isNodeSelection(selection)) {
        return null;
    }
    const nodes = selection.getNodes();
    const node = nodes[0];
    return $isImageNode(node) ? node : null;
}

function getDragImageData(event: DragEvent): null | InsertImagePayload {
    const dragData = event.dataTransfer?.getData('application/x-lexical-drag');
    if (!dragData) {
        return null;
    }
    const {type, data} = JSON.parse(dragData);
    if (type !== 'image') {
        return null;
    }

    return data;
}

declare global {
    interface DragEvent {
        rangeOffset?: number;
        rangeParent?: Node;
    }
}

function canDropImage(event: DragEvent): boolean {
    const target = event.target;
    return !!(
        isHTMLElement(target) &&
        !target.closest('code, span.editor-image') &&
        isHTMLElement(target.parentElement) &&
        target.parentElement.closest('div.ContentEditable__root')
    );
}

function getDragSelection(event: DragEvent): Range | null | undefined {
    let range;
    const domSelection = getDOMSelectionFromTarget(event.target);
    if (document.caretRangeFromPoint) {
        range = document.caretRangeFromPoint(event.clientX, event.clientY);
    } else if (event.rangeParent && domSelection !== null) {
        domSelection.collapse(event.rangeParent, event.rangeOffset || 0);
        range = domSelection.getRangeAt(0);
    } else {
        throw Error(`Cannot get the selection when dragging`);
    }

    return range;
}
